package cz.possoft.client.clovece.gui.components;

import java.awt.Color;
import java.awt.Graphics;
import java.util.HashMap;
import java.util.Map;

import cz.possoft.client.gui.gamepanel.components.GamePane;
import cz.possoft.generic.utils.GenericUtils;
import cz.possoft.serializable.clovece.game.CloveceField;
import cz.possoft.serializable.clovece.game.ClovecePole;
import cz.possoft.serializable.generic.Location;

/**
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 */
public class ClovecePane extends GamePane<ClovecePole>
{
   private static final long serialVersionUID = 89238942767l;
   
   private static final int AVERAGE_OF_CIRCLE = 26;
   private static final int SPACE_SIZE = 10;
   private static final int BORDER = 9;
   
   private static final Color WHITE_PLAYER_COLOR = Color.BLUE;
   private static final Color BLACK_PLAYER_COLOR = Color.RED;
   
   private Map<Integer, CloveceFigurkaOnPane> figurkyToPain;
   
   // indicator of figurka, which is selected for play. If no figurka is selected for play, then result is null. 
   private CloveceFigurkaOnPane selectedFigurkaForPlay;   
   
   public void init(ClovecePole hPole)
   {  
      setHraciPole(hPole);        
      addMouseListener(new ClovecePaneListener());
      addMouseMotionListener(new ClovecePaneListener());
   } 
   
   @Override
   public void setHraciPole(ClovecePole clovecePole)
   {
      initFigurkyToPain(clovecePole);
      super.setHraciPole(clovecePole);
   }      
   
   public CloveceFigurkaOnPane getSelectedFigurkaForPlay()
   {
      return selectedFigurkaForPlay;
   }

   public void setSelectedFigurkaForPlay(CloveceFigurkaOnPane selectedFigurkaForPlay)
   {
      this.selectedFigurkaForPlay = selectedFigurkaForPlay;
   }

   /**
    * Helper method, which is called during initialization (which usually happen after set Clovece as game or during startup of game)
    * 
    * @param hPole
    */
   private void initFigurkyToPain(ClovecePole hPole)
   {
      // init figurky, which will be paint
      figurkyToPain = new HashMap<Integer, CloveceFigurkaOnPane>();      
      for (int i=0 ; i<ClovecePole.LENGTH ; i++)
      {
         CloveceField field = hPole.getField(i);
         Location loc = CloveceFigurkaOnPane.BASIC_FIELD_LOCATIONS.get(i);
         figurkyToPain.put(field.getId(), new CloveceFigurkaOnPane(field, loc));
      }            
      for (int i=0 ; i<4 ; i++)
      {
         CloveceField wPlayerHomeField = hPole.getHomeField(i, GenericUtils.COLOR_WHITE);
         Location loc = CloveceFigurkaOnPane.WHITE_PLAYER_HOME_FIELD_LOCATIONS.get(i);
         figurkyToPain.put(wPlayerHomeField.getId(), new CloveceFigurkaOnPane(wPlayerHomeField, loc));                  
      }    
      for (int i=0 ; i<4 ; i++)
      {
         CloveceField bPlayerHomeField = hPole.getHomeField(i, GenericUtils.COLOR_BLACK);
         Location loc = CloveceFigurkaOnPane.BLACK_PLAYER_HOME_FIELD_LOCATIONS.get(i);
         figurkyToPain.put(bPlayerHomeField.getId(), new CloveceFigurkaOnPane(bPlayerHomeField, loc));                  
      }        
      for (int i=0 ; i<4 ; i++)
      {
         CloveceField wPlayerStartField = hPole.getStartField(i, GenericUtils.COLOR_WHITE);
         Location loc = CloveceFigurkaOnPane.WHITE_PLAYER_START_FIELD_LOCATIONS.get(i);
         figurkyToPain.put(wPlayerStartField.getId(), new CloveceFigurkaOnPane(wPlayerStartField, loc));                  
      }    
      for (int i=0 ; i<4 ; i++)
      {
         CloveceField bPlayerStartField = hPole.getStartField(i, GenericUtils.COLOR_BLACK);
         Location loc = CloveceFigurkaOnPane.BLACK_PLAYER_START_FIELD_LOCATIONS.get(i);
         figurkyToPain.put(bPlayerStartField.getId(), new CloveceFigurkaOnPane(bPlayerStartField, loc));                   
      } 
      
      // init neighbours for CloveceFigurkaOnPane items
      for (CloveceFigurkaOnPane currentFig : figurkyToPain.values())
      {
         CloveceFigurkaOnPane neighbourOfCurrent = findNeighbourFrom(currentFig);
         currentFig.setNeighbourFrom(neighbourOfCurrent);
      }      
   }
   
   /**
    * return figurka by id.
    * 
    * @param id
    * @return figurkaOnPane with given id.
    */
   public CloveceFigurkaOnPane getFigurkaOnPaneById(int id)
   {
      return figurkyToPain.get(id);
   }
   
   /**
    * Clearing of rings on all figurkas.
    *
    */
   void clearRings()
   {
      for (CloveceFigurkaOnPane clFigurka : figurkyToPain.values())
      {
         if (clFigurka.getRingColor() != GenericUtils.COLOR_NEUTRAL)
         {
            clFigurka.setRingColor(GenericUtils.COLOR_NEUTRAL);
         }
      }
   }
   
   // Painting of whole pane  
   @Override
   public void paint(Graphics g)  
   {
      super.paint(g);
      
      // fill whole pane with white color
      g.setColor(Color.WHITE);
      g.fillRect(0,0,450,450);
      
      // rectangle
      g.setColor(Color.BLACK);
      g.drawRect(1, 1, 448, 448);
      g.drawRect(10, 10, 430, 430);
    
      g.setColor(WHITE_PLAYER_COLOR);
      g.fillArc(BORDER + SPACE_SIZE*(2) - 10, BORDER + SPACE_SIZE*(11) + AVERAGE_OF_CIRCLE*9 - 10, AVERAGE_OF_CIRCLE*2 + 30, AVERAGE_OF_CIRCLE*2 + 30, 0, 360);
      g.fillRect(BORDER + SPACE_SIZE*(2+5) + AVERAGE_OF_CIRCLE*5 - 5, BORDER + SPACE_SIZE*(2+6) + AVERAGE_OF_CIRCLE*6 - 5, AVERAGE_OF_CIRCLE + SPACE_SIZE, (AVERAGE_OF_CIRCLE + SPACE_SIZE) * 4);
    
      g.setColor(BLACK_PLAYER_COLOR);
      g.fillArc(BORDER + SPACE_SIZE*(11) + AVERAGE_OF_CIRCLE*9 - 10, BORDER + SPACE_SIZE*(2) - 10, AVERAGE_OF_CIRCLE*2 + 30, AVERAGE_OF_CIRCLE*2 + 30, 0, 360);
      g.fillRect(BORDER + SPACE_SIZE*(2+5) + AVERAGE_OF_CIRCLE*5 - 5, BORDER + SPACE_SIZE*(2+1) + AVERAGE_OF_CIRCLE*1 - 5, AVERAGE_OF_CIRCLE + SPACE_SIZE, (AVERAGE_OF_CIRCLE + SPACE_SIZE) * 4);      
    
      for (CloveceFigurkaOnPane figurka : figurkyToPain.values())
      {
         paintFigurka(figurka, g);  
         if (figurka.getNeighbourFrom() != null)
         {
            paintLineBetweenTwoFields(figurka, figurka.getNeighbourFrom(), g);
         }           
      }
   }
   
   private void paintFigurka(CloveceFigurkaOnPane figurka, Graphics g) 
   {
      Location loc = figurka.getLocation();
            
      // Need to fill white circles for fields which are in home or start fields
      if ((figurka.getField().isPartOfHome(GenericUtils.COLOR_BLACK)) || (figurka.getField().isPartOfHome(GenericUtils.COLOR_WHITE)) ||
            (figurka.getField().isPartOfStart(GenericUtils.COLOR_BLACK)) || (figurka.getField().isPartOfStart(GenericUtils.COLOR_WHITE)))         
      {
         g.setColor(Color.WHITE);
         g.fillArc(BORDER + SPACE_SIZE*(2+loc.getX()) + AVERAGE_OF_CIRCLE*loc.getX(), BORDER + SPACE_SIZE*(2+loc.getY()) + AVERAGE_OF_CIRCLE*loc.getY(), AVERAGE_OF_CIRCLE, AVERAGE_OF_CIRCLE, 0, 360);         
      }
      
      // draw basic arc
      g.setColor(Color.GRAY);            
      g.drawArc(BORDER + SPACE_SIZE*(2+loc.getX()) + AVERAGE_OF_CIRCLE*loc.getX(), BORDER + SPACE_SIZE*(2+loc.getY()) + AVERAGE_OF_CIRCLE*loc.getY(), AVERAGE_OF_CIRCLE, AVERAGE_OF_CIRCLE, 0, 360);      
      
      // change colors for startup fields.
      if (figurka.getField().getStartFieldOnBoard() == GenericUtils.COLOR_WHITE)
      {         
         g.setColor(WHITE_PLAYER_COLOR);
      }
      else if (figurka.getField().getStartFieldOnBoard() == GenericUtils.COLOR_BLACK)
      {         
         g.setColor(BLACK_PLAYER_COLOR);
      }
      
      g.drawArc(-1 + BORDER + SPACE_SIZE*(2+loc.getX()) + AVERAGE_OF_CIRCLE*loc.getX(), -1 + BORDER + SPACE_SIZE*(2+loc.getY()) + AVERAGE_OF_CIRCLE*loc.getY(), AVERAGE_OF_CIRCLE + 2, AVERAGE_OF_CIRCLE + 2, 0, 360);
      
      // some necessary things for startup fields
      if (figurka.getField().getStartFieldOnBoard() != GenericUtils.COLOR_NEUTRAL)
      {
         paintAndFillEllipse(g, loc);
      }
      
      // draw rings but only if there is no figurka selected for play
      if (figurka.getRingColor() != GenericUtils.COLOR_NEUTRAL)
      {
         g.setColor(Color.GREEN);
         paintAndFillEllipse(g, loc);
      }
      
      // draw ring for 'selected for play'
      if (figurka.getRingSelectedForPlay() != GenericUtils.COLOR_NEUTRAL)
      {
         g.setColor(Color.GREEN);
         paintAndFillEllipse(g, loc);         
      }   
      
      // draw rings for places where we can play
      if (figurka.getRingForPlaceWhereWeCanPlay() != GenericUtils.COLOR_NEUTRAL)
      {
         g.setColor(Color.YELLOW);
         paintAndFillEllipse(g, loc);           
      }
      
      // paint figurka on our field if figurka is really here
      if (figurka.getField().getColor() != GenericUtils.COLOR_NEUTRAL)
      {
         paintRealFigurka(loc.getX(), loc.getY(), figurka.getField().getColor(), g);
      }      
   }
   
   /**
    * Painting of ellipse. It assumes that needed color is already set before calling this method. 
    *  
    * @param g
    */
   private void paintAndFillEllipse(Graphics g, Location loc)
   {
      g.fillArc(-2 + BORDER + SPACE_SIZE*(2+loc.getX()) + AVERAGE_OF_CIRCLE*loc.getX(), -2 + BORDER + SPACE_SIZE*(2+loc.getY()) + AVERAGE_OF_CIRCLE*loc.getY(), AVERAGE_OF_CIRCLE + 4, AVERAGE_OF_CIRCLE + 4, 0, 360);         
      g.setColor(Color.WHITE);
      g.fillArc(2 + BORDER + SPACE_SIZE*(2+loc.getX()) + AVERAGE_OF_CIRCLE*loc.getX(), 2 + BORDER + SPACE_SIZE*(2+loc.getY()) + AVERAGE_OF_CIRCLE*loc.getY(), AVERAGE_OF_CIRCLE - 4, AVERAGE_OF_CIRCLE - 4, 0, 360);               
   }
   
   private void paintLineBetweenTwoFields(CloveceFigurkaOnPane figurka1, CloveceFigurkaOnPane figurka2, Graphics g)
   {
      Location loc1 = figurka1.getLocation();
      Location loc2 = figurka2.getLocation();
      
      // compute middle point of circles
      g.setColor(Color.GRAY);
      int x1 = BORDER + SPACE_SIZE*(2+loc1.getX()) + AVERAGE_OF_CIRCLE*loc1.getX() + AVERAGE_OF_CIRCLE/2;
      int y1 = BORDER + SPACE_SIZE*(2+loc1.getY()) + AVERAGE_OF_CIRCLE*loc1.getY() + AVERAGE_OF_CIRCLE/2;
      int x2 = BORDER + SPACE_SIZE*(2+loc2.getX()) + AVERAGE_OF_CIRCLE*loc2.getX() + AVERAGE_OF_CIRCLE/2;
      int y2 = BORDER + SPACE_SIZE*(2+loc2.getY()) + AVERAGE_OF_CIRCLE*loc2.getY() + AVERAGE_OF_CIRCLE/2;  
           
      boolean xSameFlag = true;
      // compute beginning and end of lines
      if (x1 == x2)
      {
         if (y1 > y2)
         {
            int pom = y1;
            y1 = y2;
            y2 = pom;
         }
         
         y1 = y1 + AVERAGE_OF_CIRCLE/2 + 2;
         y2 = y2 - AVERAGE_OF_CIRCLE/2 - 2;
      }
      else if (y1 == y2)
      {
         xSameFlag = false;
         if (x1 > x2)
         {
            int pom = x1;
            x1 = x2;
            x2 = pom;
         }
         
         x1 = x1 + AVERAGE_OF_CIRCLE/2 + 2;
         x2 = x2 - AVERAGE_OF_CIRCLE/2 - 2;         
      }
      
      // draw lines
      if (xSameFlag)
      {
         g.drawLine(x1, y1, x2, y2);
         g.drawLine(x1 - 1, y1, x2 - 1, y2);
         g.drawLine(x1 + 1, y1, x2 + 1, y2);
      }
      else
      {
         g.drawLine(x1, y1, x2, y2);
         g.drawLine(x1, y1 - 1, x2, y2 - 1);
         g.drawLine(x1, y1 + 1, x2, y2 + 1);         
      }
   }
   
   /**
    * Paint real figurka but only in case that figurka is really here.
    *  
    * @param x
    * @param y
    * @param color
    * @param g
    */
   private void paintRealFigurka(int x, int y, int color, Graphics g)
   {      
      Color col = color == GenericUtils.COLOR_WHITE ? WHITE_PLAYER_COLOR : BLACK_PLAYER_COLOR;      
      g.setColor(col);
      
      // TODO: do some real painting      
      g.drawLine(BORDER + SPACE_SIZE*(2+x) + AVERAGE_OF_CIRCLE*x + 5, 
            BORDER + SPACE_SIZE*(2+y) + AVERAGE_OF_CIRCLE*y + 5, 
            BORDER + SPACE_SIZE*(2+x) + AVERAGE_OF_CIRCLE*x + 20, 
            BORDER + SPACE_SIZE*(2+y) + AVERAGE_OF_CIRCLE*y + 20);                        
   }
   
   /**
    * Helper method for finding neighbour for paint of "current".
    * 
    * @param current
    * @return
    */
   private CloveceFigurkaOnPane findNeighbourFrom(CloveceFigurkaOnPane current)
   {
      CloveceField neighbourField = current.getField().getNeigborFromDirection();  
      
      // return if we are sure that we have no neighbour
      if (neighbourField == null)
      {
         return null;
      }
      
      // now iterate and find which figg has neighbourField as field. This is our neighbour.
      for (CloveceFigurkaOnPane figg : figurkyToPain.values())
      {
         CloveceField neighb = figg.getField();
         if (neighb.equals(neighbourField))
         {
            return figg;
         }
      }
      
      throw new IllegalStateException("Can't find neighbour of fig " + current + " from location " + current.getLocation());
   }

}

